#include "vm/frame.h"
#include "vm/swap.h"

static struct block *swap_device;
static struct bitmap *swap_map;
static struct lock swap_lock;

bool
swap_init ()
{
  printf("in init");
  lock_init (&swap_lock);
  swap_device = block_get_role (3);
  if (NULL == swap_device)
    {
      return false;
    }
  int swap_map_size = block_size (swap_device);
  swap_map = bitmap_create (swap_map_size);
  if(NULL != swap_map)
    return true;
  else
    return false;
}

bool
swap_in (struct frame_struct *fs)
{  
  size_t length = fs->page_length;
  block_sector_t sector = fs->sector_number;
  uint8_t * kpage = palloc_get_page (PAL_USER | PAL_ZERO);
  if (fs->flag == DISK)
    read_device = fs_device;

  block_sector_t i;
  //printf ("reading for address %p virtually mapped to %p\n", fs->upage, fs->vaddr);
  for (i=0; i < PGSIZE/BLOCK_SECTOR_SIZE;i++)
    {
      block_read (read_device, sector + i, kpage+BLOCK_SECTOR_SIZE * i);
    }
  if (length < PGSIZE)
    memset (kpage + length, 0, PGSIZE - length);

  if (!install_page(fs->upage, kpage, fs->writable))
    {
      printf ("returing false\n");
      return false;
    }
  
  struct thread *current = thread_current ();
  fs->vaddr = kpage;
  //printf ("virtual address set to %p\n", fs->vaddr);
  pagedir_activate (current->pagedir);
  return true;
}

/* Given a frame struct, this method swaps it out to the swap disk*/
bool
swap_out (struct frame_struct *fs)
{
  block_sector_t sector_number = 0;
  uint8_t *kernel_page = NULL;
  uint8_t *user_page = NULL;

  //get the virtual address to be swapped out
  kernel_page = fs->vaddr;
  user_page = fs->upage;
  
  //if the kernel page is null, we cannot proceed further, hence return false  
  if (NULL == kernel_page)
    return false;
  
  //we do not proceed further if the length of the page is zero or if the 
  //  page is not dirty
  if (0 == fs->page_length && 
      false == pagedir_is_dirty (thread_current ()->pagedir, user_page))
    return true;
  
  
  //swap only stack and memory frames
  if (fs->flag == STACK || fs->flag == MEMORY)
  {
    //swap only if the page is dirty
    if (true == pagedir_is_dirty (thread_current ()->pagedir, user_page))
      {
        lock_acquire (&swap_lock);
        sector_number = 
            bitmap_scan_and_flip (swap_map, 0, PGSIZE/BLOCK_SECTOR_SIZE, false);
        
        int counter = 0;
        for (; counter < PGSIZE/BLOCK_SECTOR_SIZE; counter++)
          block_write (swap_device, sector_number + counter, 
		       kernel_page + BLOCK_SECTOR_SIZE * counter);

        pagedir_clear_page (thread_current ()->pagedir, user_page);
        lock_release (&swap_lock);
     }    
  }
}
